﻿#region Creditos

/* Autor: Edilberto Sánchez Forero	*/
/* Email: esanchezfo@gmail.com		*/
/* Licencia: LGPL, Software Libre	*/
/* País: Colombia, Bogotá			*/

#endregion

#region Librerias

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.HtmlControls;
using System.Web.UI;
using System.Web;
using System.Linq;
using System.Collections.Specialized;

using ESF.General;

using Microsoft.Reporting.WebForms;
using System.IO;
using Ionic.Zip;
using ESF.Web;
using ESF.MSReporting.WS;
using System.Web.UI.WebControls;
using System.Data;
using System.Reflection;
using System.Security;
using System.Security.Permissions;

#endregion


namespace ESF.MSReporting
{
	public static class MSReportingHelper
	{
		#region Constantes
	
		public const string QR_AssemblyReference = "MessagingToolkit.QRCode, Version=1.3.0.0, Culture=neutral, PublicKeyToken=cc67cd43e4c7f109";
		public const string Drawing_AssemblyReference = "System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
		public const string BarCode1D_AssemblyReference = "Zen.Barcode.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=b5ae55aa76d2d9de";
		public const string ESFGeneral_AssemblyReference = "ESF.General, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6f4a61afe7bee85f";
		
		private const string SESSION_REPORT_PARAMS_BASEKEY = "ESF.MSReporting.MSReportingHelper.Params.{0}";
		private const string SESSION_REPORT_CONFIG_BASEKEY = "ESF.MSReporting.MSReportingHelper.Configuration.{0}";
		
		public const string REPORTVIEWER_FORMURL = "ESF.MSReporting.ReportViewer.FormUrl";
		public const string REPORTVIEWER_PROCESSINGMODE = "ESF.MSReporting.ReportViewer.ProcessingMode";
		
		#endregion
		
		
		#region Constantes de configuracion
		
		public const string ALTERNATEDOWNLOADURL_PARAM = "AlternateDownloadURL";
		public const string REPORTPATH_PARAM = "ReportPath";

		public const string REPORTSERVER_URL_KEY = "ReportServerURL";
		public const string REPORTSERVER_USERNAME_KEY = "ReportServerUserName";
		public const string REPORTSERVER_USERPASSWORD_KEY = "ReportServerUserPassword";
		public const string REPORTSERVER_USERDOMAIN_KEY = "ReportServerUserDomain";
		public const string REPORTSERVER_FOLDERPATH = "ReportServerFolderPath";
		public const string REPORTSERVER_RENDERFOLDERMAXSIZEMEGAS = "ReportServerRenderFolderMaxSizeMegas";
		public const string REPORTSERVER_RENDERFOLDERMAXOLDFILEMINUTES = "ReportServerRenderFolderMaxOldFileMinutes";
		//public const string REPORTSERVER_ASSEMBLYREFERENCES = "ReportAssemblyReferences";

		#endregion


		#region Propiedades

		public static int RenderFolderMaxSizeMegas
		{
			get
			{
				return ReadConfig.GetAppSettingInt32 (MSReportingHelper.REPORTSERVER_RENDERFOLDERMAXSIZEMEGAS, 1500);
			}
		}


		public static int RenderFolderMaxFileMinutes
		{
			get
			{
				return ReadConfig.GetAppSettingInt32 (MSReportingHelper.REPORTSERVER_RENDERFOLDERMAXOLDFILEMINUTES, 48 * 60);
			}
		}

		#endregion
		
		
		#region Funciones
		
		public static void DeleteOldFilesZipNewFile (string pReportTitle, string pDataFilename, string pURLDataFilename, DateTime pRequestStart, List<Microsoft.Reporting.WebForms.ReportParameter> pParametersResumen, bool pDoZip, bool pDoZipPassword, bool pDeleteOld, string pAlternateDownloadURL, ref StringBuilder pResultMessage)
		{
			NameValueCollection parametersResumen;

			
			if (pParametersResumen == null || pParametersResumen.Count == 0)
			{
				parametersResumen = null;
			}
			else
			{
				parametersResumen = new NameValueCollection (pParametersResumen.Count);

				foreach (Microsoft.Reporting.WebForms.ReportParameter param in pParametersResumen)
				{
					parametersResumen.Add (param.Name, param.Values [0]);
				}
			}

			DeleteOldFilesZipNewFile (pReportTitle, pDataFilename, pURLDataFilename, pRequestStart, parametersResumen, pDoZip, pDoZipPassword, pDeleteOld, pAlternateDownloadURL, ref pResultMessage);
		}


		public static void DeleteOldFilesZipNewFile (string pReportTitle, string pDataFilename, string pURLDataFilename, DateTime pRequestStart, NameValueCollection pParametersResumen, bool pDoZip, bool pDoZipPassword, bool pDeleteOld, string pAlternateDownloadURL, ref StringBuilder pResultMessage)
		{
			string zipFilename;
			string zipPassword = string.Empty;

			long zipLength = 0;

			bool isThereParams;


			#region Limpieza del Directorio

			if (pDoZip)
			{
				MSReportingHelper.DeleteOldFilesZipNewFile (
					pDataFilename, pDoZipPassword, pDeleteOld,
					out zipFilename, out zipLength, out zipPassword);
			}

			#endregion

			#region Mensaje

			pResultMessage.AppendFormat ("El Reporte {0} solicitado en {1} ha sido generado.<br/><br/>Fue ejecutado con los siguientes parametros: ", 
					pReportTitle, pRequestStart);

				if (pParametersResumen == null || pParametersResumen.Count == 0)
				{
					pResultMessage.AppendLine ("Ninguno");
				}
				else
				{
					isThereParams = false;

					pResultMessage.AppendLine ("<br/>");

					foreach (string key in pParametersResumen.AllKeys)
					{
						if (!string.IsNullOrEmpty (pParametersResumen [key]) && !key.StartsWith ("pRender") && key != ReportParams.RESUME_PARAMNAME)
						{
							pResultMessage.AppendFormat ("<li>{0}: {1}</li>", key, pParametersResumen [key]);

							isThereParams = true;
						}
					}

					if (!isThereParams)
					{
						pResultMessage.AppendLine ("Ninguno");
					}
				}

			pResultMessage.AppendFormat ("<br /><br />Es un archivo en formato {0}", Path.GetExtension (pDataFilename).ToUpper ());
			
			if (pDoZip)
			{
				pResultMessage.AppendFormat (", esta comprimido en formato ZIP, tiene un tamaño de {0}.", TextHelper.ToStringBytes(zipLength));
			
				if (pDoZipPassword)
				{ 
					pResultMessage.AppendFormat ("<br/><br/>Por cuestiones de seguridad requiere de la contraseña <b>{0}</b> para ser abierto.", zipPassword);
				}
			}
			else
			{
				pResultMessage.AppendFormat (", tiene un tamaño de {0}.", TextHelper.ToStringBytes(new FileInfo (pDataFilename).Length));
			}
				
			if (pDeleteOld)
			{
				pResultMessage.AppendFormat ("<br/><br/>El archivo esta disponible por un termino mínimo de {0:##} horas por cuestiones de almacenamiento.", RenderFolderMaxFileMinutes / 60.0);
			}

			pResultMessage.AppendLine ("<br /><br />Puede descargarlo dando click en el siguiente enlace:");

			if (!string.IsNullOrEmpty (pAlternateDownloadURL))
			{
				pResultMessage.AppendFormat ("<br/><br /><a href=\"{0}.zip\">Página de Descarga del Reporte {1}</a>", pAlternateDownloadURL, pReportTitle);
			}
			else 
			{
				if (pDoZip)
				{
					pResultMessage.AppendFormat ("<br/><br /><a href=\"{0}.zip\">Reporte Generado {1}</a>", pURLDataFilename, pReportTitle);
				}
				else
				{
					pResultMessage.AppendFormat ("<br/><br /><a href=\"{0}\">Reporte Generado {1}</a>", pURLDataFilename, pReportTitle);
				}
			}

			#endregion
		}


		public static void DeleteOldFilesZipNewFile (string pDataFilename, bool pDoPassword, bool pDeleteOld, out string pZipDataFilename, out long pZipDataLength, out string pZipPassword)
		{
			pZipDataFilename = pDataFilename + ".zip";
			

			#region Limpieza del Directorio

			if (pDeleteOld)
			{
				if (FileHelper.IsDirectoryOverMaxSize (Path.GetDirectoryName (pDataFilename), RenderFolderMaxSizeMegas))
				{
					FileHelper.DeleteOldFiles (Path.GetDirectoryName (pDataFilename), DateTime.Now.AddMinutes (-1 * RenderFolderMaxFileMinutes));
				}
			}

			#endregion
						
						
			#region Compresion

			pZipPassword = string.Empty;

			using (ZipFile zip = new ZipFile (pZipDataFilename))
			{
				if (pDoPassword)
				{
					pZipPassword = PasswordHelper.Generate ();
					
					zip.Password = pZipPassword;
				}

				zip.AddFile (pDataFilename, string.Empty);

				zip.Save ();
			}

			File.Delete (pDataFilename);

			#endregion


			pZipDataLength = new FileInfo (pZipDataFilename).Length;
		}

		
		public static List<Microsoft.Reporting.WebForms.ReportParameter> GetSessionPassedReportParameters (Page pPage, string pGuid)
		{
			List<Microsoft.Reporting.WebForms.ReportParameter> result;


			result = PageHelper.GetRemoveSession (pPage, string.Format(SESSION_REPORT_PARAMS_BASEKEY, pGuid)) as List<Microsoft.Reporting.WebForms.ReportParameter>;

			return result;
		}
		
		
		public static NameValueCollection GetSessionPassedReportConfig (Page pPage, string pGuid)
		{
			NameValueCollection result;


			result = PageHelper.GetRemoveSession (pPage, string.Format(SESSION_REPORT_CONFIG_BASEKEY, pGuid)) as NameValueCollection;

			return result;
		}


		public static void HideReportIframe(HtmlControl pIframeReporte)
		{
			HideReportIframe(pIframeReporte, false);
		}


		public static void HideReportIframe(HtmlControl pIframeReporte, bool pHideWithParent)
		{
			pIframeReporte.Attributes["src"] = string.Empty;

			pIframeReporte.Visible = false;

			if (pHideWithParent && pIframeReporte.Parent != null)
			{
				pIframeReporte.Parent.Visible = false;
			}
		}
		
		
		public static void ShowLocalReportIframeBasic (HtmlControl pIframeReporte, 
			string pReportPath, string pFormPath, object pExtraData = null, 
			List<Microsoft.Reporting.WebForms.ReportParameter> pParams = null, 
			bool? pRenderDirectly = null, 
			ReportParams.ExportFormat? pExportFormat = null)
		{
			ShowReportIframeBasic (pIframeReporte, null, pReportPath, pFormPath, ProcessingMode.Local, pExtraData, pParams, pRenderDirectly, pExportFormat);
		}
		
		
		public static void ShowReportIframeBasic 
		(
			HtmlControl pIframeReporte, 
			string pReportServerFolderPath, 
			string pReportPath, 
			string pFormPath, 
			ProcessingMode pProcessingMode = ProcessingMode.Remote, 
			object pExtraData = null,
			List<Microsoft.Reporting.WebForms.ReportParameter> pParams = null,
			bool? pRenderDirectly = null, 
			ReportParams.ExportFormat? pExportFormat = null
		)
		{
			ShowReportIframe (pIframeReporte, 
				ReadConfig.GetAppSetting (REPORTSERVER_URL_KEY, string.Empty),
				pReportServerFolderPath,
				ReadConfig.GetAppSetting (REPORTSERVER_USERNAME_KEY, string.Empty), 
				ReadConfig.GetAppSetting (REPORTSERVER_USERPASSWORD_KEY, string.Empty),
				ReadConfig.GetAppSetting (REPORTSERVER_USERDOMAIN_KEY, string.Empty),
				pReportPath,
				pFormPath,
				pProcessingMode,
				pExtraData,
				pParams,
				pRenderDirectly, 
				pExportFormat);
		}
		
		
		public static void ShowReportIframe (HtmlControl pIframeReporte, 
			string pReportServerURL, string pReportServerFolderPath,
			string pReportServerUserName, string pReportServerUserPassword,
			string pReportServerUserDomain,
			string pReportPath, string pFormPath, 
			ProcessingMode pProcessingMode = ProcessingMode.Remote,
			object pExtraData = null,
			List<Microsoft.Reporting.WebForms.ReportParameter> pParams = null,
			bool? pRenderDirectly = null, 
			ReportParams.ExportFormat? pExportFormat = null)
		{
			string guid;
			
			NameValueCollection config;
			

			HttpContext.Current.Trace.Write ("ShowReportIframe Start");
			
			Validate.FilledObject (true, "IframeReporte", pIframeReporte);
			Validate.StartsWith ("FormPath", pFormPath, "~/");
			
			pFormPath = VirtualPathUtility.ToAbsolute (pFormPath);
			
			pIframeReporte.Visible = true;
			
			if (pIframeReporte.Parent != null)
			{
				pIframeReporte.Parent.Visible = true;
			}
			

			#region Javascript para dimensionar

			JS.AddEndJS(pIframeReporte.Page, @"ShowLoadingMessage (); FixSizeReportOnload = function (pReallyHeight) 
				{
					var ifrReportViewer = $('#" + pIframeReporte.ClientID + @"');

					var height = 220;
					var tempHeight;
					

					tempHeight = (IsIE ? ifrReportViewer[0].contentWindow.document.body.scrollHeight : ifrReportViewer[0].contentWindow.document.documentElement.offsetHeight);

					if (typeof (pReallyHeight) == ""undefined"" || isNaN (pReallyHeight))
					{
						pReallyHeight = 0;
					}
					
					if (tempHeight > pReallyHeight)
					{
						height = tempHeight;
					}
					else
					{
						height = pReallyHeight;
					}

					if (height < tempHeight)
					{
						height = tempHeight
					}


					ifrReportViewer.height(height + 40);

					FixContentSizes ();
				}");

			#endregion


			guid = Guid.NewGuid ().ToString ();
		
			
			HttpContext.Current.Trace.Write ("Adding config params...");
			
			config = new NameValueCollection ();
			

			config.Add (REPORTSERVER_URL_KEY, pReportServerURL.ToString ());
			config.Add (REPORTSERVER_FOLDERPATH, pReportServerFolderPath);
			config.Add (REPORTSERVER_USERNAME_KEY, pReportServerUserName);
			config.Add (REPORTSERVER_USERPASSWORD_KEY, pReportServerUserPassword);
			config.Add (REPORTSERVER_USERDOMAIN_KEY, pReportServerUserDomain);
			config.Add (REPORTVIEWER_PROCESSINGMODE, pProcessingMode.ToString ());
			
			config.Add ("ReportPath", pReportPath);

			//config.Add (REPORTSERVER_ASSEMBLYREFERENCES, TextHelper.Concatenate ("|", pAssemblyReferences));

			if (pRenderDirectly != null && pParams != null && !pParams.Exists(p => p.Name == ReportParams.RENDER_DIRECTLY))
			{
				pParams.Add(new Microsoft.Reporting.WebForms.ReportParameter (ReportParams.RENDER_DIRECTLY, pRenderDirectly.ToString ()));
			}

			if (pExportFormat != null && pParams != null && !pParams.Exists(p => p.Name == ReportParams.RENDERAS_PARAMNAME))
			{
				pParams.Add(new Microsoft.Reporting.WebForms.ReportParameter(ReportParams.RENDERAS_PARAMNAME, pExportFormat.ToString()));
			}


			HttpContext.Current.Trace.Write ("Setting session params...");

			pIframeReporte.Page.Session [string.Format (SESSION_REPORT_PARAMS_BASEKEY, guid)] = pParams;
			pIframeReporte.Page.Session [string.Format (SESSION_REPORT_CONFIG_BASEKEY, guid)] = config;
			

			pIframeReporte.Attributes ["onunload"] = "ShowLoadingMessage ();";
			pIframeReporte.Attributes ["onload"] = "HideLoadingMessage ();";
			pIframeReporte.Attributes ["style"] = "height: 600px;";

			HttpContext.Current.Trace.Write ("Adding extradata... null ? " + (pExtraData == null));

			pIframeReporte.Page.Session ["ExtraData" + guid] = pExtraData;
			

			JS.AddEndJS (pIframeReporte.Page, string.Format ("$('#{0}').attr ('src', '{1}?GUID={2}');", 
				pIframeReporte.ClientID, pFormPath, guid));

			HttpContext.Current.Trace.Write ("Iframe URL: " + pIframeReporte.Attributes ["src"]);

			HttpContext.Current.Trace.Write("ShowReportIframe End");
		}
		
		
		public static void PrepareReport (ReportExecutionService pWS, string pReportFolder, string pReportPath, List<Microsoft.Reporting.WebForms.ReportParameter> pParameters)
		{
			ESF.MSReporting.WS.ReportParameter [] reportParams;

			List<ESF.MSReporting.WS.ParameterValue> definitiveParams;
			Microsoft.Reporting.WebForms.ReportParameter tmpOtherParam;

			ExecutionInfo execInfo;
		
		
			pWS.ExecutionHeaderValue = new ExecutionHeader ();


			definitiveParams = new List<ESF.MSReporting.WS.ParameterValue> ();
			
			#region Ajustes
			
			pReportPath = ("/" + pReportFolder + "/" + pReportPath).Replace ("//", "/");

			#endregion

			execInfo = pWS.LoadReport (pReportPath, null);

			reportParams = execInfo.Parameters;
			

			if (pParameters != null)
			{
				// Se excluyen los parametros no soportados
				foreach (ESF.MSReporting.WS.ReportParameter rpi in reportParams)
				{
					tmpOtherParam = pParameters.Find (p => p.Name == rpi.Name);

					if (tmpOtherParam != null)
					{
						definitiveParams.Add (new ESF.MSReporting.WS.ParameterValue () { Name = tmpOtherParam.Name, Value = tmpOtherParam.Values [0] });
					}
				}
			}
			
			if (definitiveParams.Count == 0)
			{
				definitiveParams = new List<ESF.MSReporting.WS.ParameterValue> ();
			
				foreach (ESF.MSReporting.WS.ReportParameter r in reportParams)
				{
					if (r.Name == ReportParams.RESUME_PARAMNAME)
					{
						definitiveParams.Add (new ESF.MSReporting.WS.ParameterValue () { Name = ReportParams.RESUME_PARAMNAME, Value = "Ninguno" });
					}
				}
			}
			
			pWS.SetExecutionParameters (definitiveParams.ToArray (), null);			
		}


		public static void PrepareReport (ReportViewer pReportViewer, string pReportFolder, string pReportPath, List<Microsoft.Reporting.WebForms.ReportParameter> pParameters)
		{
			PrepareReport (pReportViewer, pReportFolder, pReportPath, pParameters, null);
		}


		public static void PrepareReport (ReportViewer pReportViewer, string pReportFolder, string pReportPath, List<Microsoft.Reporting.WebForms.ReportParameter> pParameters, object pDatos)
		{
			int index;

			DataSet theDataSet;

			
			pReportViewer.Page.Trace.Write ("Preparing reportviewer: Start");

			if (DataHelper.IsEmpty(pDatos) && pReportViewer.ProcessingMode == ProcessingMode.Local)
			{
				pReportViewer.Page.Trace.Write ("Empty local data");

				JS.AddWarning(pReportViewer.Page, "No fueron encontrados registros con los parametros indicados");
				pReportViewer.LocalReport.ReportPath = string.Empty;
				pReportViewer.Visible = false;
				
				return;
			}


			if (pReportViewer.ProcessingMode == ProcessingMode.Remote)
			{
				pReportViewer.ServerReport.ReportPath = ("/" + pReportFolder + "/" + pReportPath).Replace ("//", "/");
			}
			else
			{
				pReportViewer.LocalReport.EnableHyperlinks = true;
				pReportViewer.LocalReport.EnableExternalImages = true;
				pReportViewer.HyperlinkTarget = "_self";

				pReportViewer.LocalReport.SetBasePermissionsForSandboxAppDomain(new PermissionSet (PermissionState.Unrestricted));

				if (string.IsNullOrEmpty (pReportViewer.LocalReport.ReportPath))
				{
					pReportViewer.LocalReport.ReportPath = pReportViewer.Page.MapPath (
						((TextHelper.IsEmpty (pReportFolder) ? string.Empty : pReportFolder + "/") + pReportPath).Replace ("//", "/") + ".rdlc");
				}
			}

			
			if (pReportViewer.ProcessingMode == ProcessingMode.Remote)
			{
				if (!DataHelper.IsEmpty (pParameters))
				{
					pReportViewer.Page.Trace.Write("Setting parameters for Remote: Start");

					pParameters = (from P in pParameters 
						join RP in pReportViewer.ServerReport.GetParameters() on P.Name equals RP.Name
						select P).ToList ();

					pReportViewer.ServerReport.SetParameters(pParameters); 

					pReportViewer.Page.Trace.Write("Setting parameters for Remote: End");
				}
			}
			else
			{
				if (!DataHelper.IsEmpty (pParameters))
				{
					pReportViewer.Page.Trace.Write("Setting parameters for Local: Start");

					pParameters = (from P in pParameters
								   join RP in pReportViewer.LocalReport.GetParameters() on P.Name equals RP.Name
								   select P).ToList();

					pReportViewer.LocalReport.SetParameters(pParameters); 

					pReportViewer.LocalReport.SetParameters(pParameters.FindAll (p=>p.Name == ReportParams.RESUME_PARAMNAME)); 

					pReportViewer.Page.Trace.Write("Setting parameters for Local: End");
				}

				pReportViewer.LocalReport.EnableExternalImages = true;

				#region Extraer Datos Locales

				if (DataHelper.IsEmpty(pDatos))
				{
					pReportViewer.Page.Trace.Warn("There is no data to show");

					pReportViewer.Visible = false;
					return;
				}


				index = 1;

				theDataSet = null;


				if (pDatos is DataSet)
				{
					theDataSet = pDatos as DataSet;	
				}
				else if (pDatos is DataTable)
				{
					theDataSet = DataHelper.ToDataSet (pDatos as DataTable);
				}


				if (theDataSet != null)
				{
					foreach (DataTable data in theDataSet.Tables)
					{
						pReportViewer.Page.Trace.Write("Setting DataSet" + index);

						pReportViewer.LocalReport.DataSources.Add(new ReportDataSource("DataSet" + index, data));
					
						index++; 
					}
				}
				else
				{
					pReportViewer.LocalReport.DataSources.Add(new ReportDataSource("DataSet1", pDatos));
				}

				pReportViewer.Page.Trace.Write("Refreshing report");
				
				pReportViewer.LocalReport.Refresh ();

				#endregion
			}

			pReportViewer.Page.Trace.Write("Preparing reportviewer: End");
		}
		
		
		public static void ShowReport (ReportViewer pReportViewer)
		{
			pReportViewer.Page.Trace.Write("Show reportviewer: Start");

			pReportViewer.KeepSessionAlive = false;
			pReportViewer.SizeToReportContent = true;

			//pReportViewer.Width = Unit.Percentage(100);
			pReportViewer.Height = Unit.Percentage (100);
			pReportViewer.Enabled = true;
			pReportViewer.Visible = true;
			
			
			pReportViewer.ShowReportBody = true;

			pReportViewer.Page.Trace.Write("Show reportviewer: End");
		} 
		
		#endregion
	}
}
